package jccr;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Base class for simplified Coordinator development.
 * @author dpet
 *
 */
public abstract class CoordinatorBase implements Coordinator {

	protected CoordinatorBase( DispatcherQueue queue, boolean persist ) {		
		
		if ( queue == null )
			throw new IllegalArgumentException();
		
		_persistent = persist;	
		_dispatcherQueue = queue;
	}
	
	public void asyncCoordinate( Object source ) {
		// TODO: don't enqueue additional coordination if one is pending or active
		_dispatcherQueue.enqueue(getCoordinator(source));		
	}		

	/**
	 * Triggers a new coordination operation, this can be overriden by
	 * any subscriber BUT normally all coordination is performed by overriding
	 * the safeCoordinate(...) method.
	 * @param source the source of the coordination request
	 * @return possible subtasks
	 */
	public Iterable<Task> coordinate( Object source ) {
		
		_lock.lock();
		
		try {							
			/* if the coordinator isn't active wait for the
			 * coordinate call with "this" as source.
			 */		
			if ( !_active && source == this ) {				
				activate();
				return null;
			}
			else if ( _active ) {				
				// dispatch coordination call to sub-class
				Iterable<Task> tasks = safeCoordinate(source);
				
				/* If the coordinator isn't persistent and client protocol is
				 * complete then deactivate this coordinator.
				 */
				if ( complete() ) {
					deactivate();
				}
				return tasks;
			}
			else {  // inactive coordinator, return 
				return null; 
			}		
			
		} finally {
			_lock.unlock();
		}
	}
	
	public boolean isActive() { 			
		return _active; 
	}
	
	public boolean isPersitant() {
		return _persistent;
	}
	
	/* Called upon the first activation of the coordinator.
	 */
	protected void activate() {
		
		assert( _lock.isHeldByCurrentThread() );
		_active = true;
	}
	
	/* Activates the current coordinator by starting a
	 * new asyncCoordinate( ... ) operation. The coordinator isn't
	 * ACTIVE until this call has arrived to coordinate( ... ).
	 */
	protected void beginActivate() {
		asyncCoordinate(this);
	}
	
	/* Checks if operation is complete, override and specialize to
	 * interact with the persistent behavior.
	 */
	protected boolean complete() {
		return !_persistent;
	}
	
	protected RawPort coordinate( RawPort port ) {
		port.setCoordinator(this);
		return port;
	}
		
	/* Disables the current coordinator
	 */
	protected void deactivate() {
		
		assert( _lock.isHeldByCurrentThread() );
		_active = false;
	}
	
	protected void execute( RawPort port, Object argument ) {
		
		try {
			port.executeRaw(argument);
		}
		catch ( Exception ex ) {
			Log.e( "execute raw port failed", ex );
		}
	}
	
	/**
	 * Factory method to get a task object used for coordination.
	 * @return the Task used for coordination
	 */
	protected Task getCoordinator( Object source ) {
		return new CoordinatorTask(source);
	}
	
	protected abstract Iterable<Task> safeCoordinate( Object source );

	protected class CoordinatorTask implements Task {
		
		public CoordinatorTask(Object source) {
			_source = source;
		}
		
		public Iterable<Task> execute() {
			return coordinate( _source );
		}
		
		Object _source;
	}
	
	boolean _active;
	DispatcherQueue _dispatcherQueue;
	ReentrantLock _lock = new ReentrantLock();
	boolean _persistent;
}
